#include "xassert.h"
#include "xformat.h"
#include <stdio.h>
#include <stdarg.h>
#include <windows.h>
#include <signal.h>

namespace xge{
namespace xge_internal{


    // Assertion Error Check
#ifdef XGE_USE_WIDE_CHAR
#define XASSERTION_INTERNAL_ERRCHECK(Boolean) (void)( (Boolean) || ( FatalAppExitW(0,_xT("XGE Assertion tool has encountered unresolvable problem,now exit the application!")) ,0 ) )
#else
#define XASSERTION_INTERNAL_ERRCHECK(Boolean) (void)( (Boolean) || ( FatalAppExitA(0,_xT("XGE Assertion tool has encountered unresolvable problem,now exit the application!")) ,0 ) )
#endif

    void Assertion( const char_t* file , const char_t* func , size_t line, const char_t* expression , const char_t* format , ... ) {

      char_t stack_buffer [ MAX_CHAR_STACK_BUFFER_SIZE ];
      const char_t* additional_information = NULL;
      Format formatter;

      if( format != NULL ) {

        va_list list;
        va_start(list,format);
        int ret = 
  #ifdef XGE_USE_WIDE_CHAR 
          vswprintf(stack_buffer,
          MAX_CHAR_STACK_BUFFER_SIZE,
          format,list
          );
  #else
          vsprintf_s(stack_buffer,
          MAX_CHAR_STACK_BUFFER_SIZE,
          format,list
          );
  #endif
        if( ret <0 ) {
          // error
          // keep addition_information = NULL ;
        } else if( ret >= MAX_CHAR_STACK_BUFFER_SIZE ) {
          XASSERTION_INTERNAL_ERRCHECK(formatter.FormatString(format,list));
          additional_information = formatter.buffer();
        } else {
          additional_information = stack_buffer;
        }
      } // format != NULL


#define XASSERT_HEADER_FORMAT _xT("FileName: %s.\n\nFunction Name: %s.\n\nLine: %d.\n\nExpression Failed: %s.\n\n")
#define XASSERT_ADDITIONAL_FORMAT _xT("Additional Information: %s.\n\n")

      if( additional_information ) {
        // ignore the return value 
        XASSERTION_INTERNAL_ERRCHECK(formatter.FormatString(XASSERT_HEADER_FORMAT XASSERT_ADDITIONAL_FORMAT , 
          file,
          func,
          line,
          expression,
          additional_information
          ));
      } else {
        XASSERTION_INTERNAL_ERRCHECK(formatter.FormatString(XASSERT_HEADER_FORMAT,
          file,
          func,
          line,
          expression
          ));
      }



#undef XASSERT_HEADER_FORMAT
#undef XASSERT_ADDITIONAL_FORMAT

#ifdef XGE_USE_WIDE_CHAR 
        // Output the error information to the VS console
        ::OutputDebugStringW(
        formatter.buffer()
        );
#else
      ::OutputDebugStringA(
        formatter.buffer()
        );
#endif

      DWORD nRet = 
#ifdef XGE_USE_WIDE_CHAR


        ::MessageBoxW(
        GetActiveWindow(),
        formatter.buffer(),
        _xT("XGameEngine Assertion Failed"),
        MB_ABORTRETRYIGNORE|MB_ICONHAND|MB_SETFOREGROUND|MB_TASKMODAL) ;
#else

        ::MessageBoxA(
        GetActiveWindow(),
        formatter.buffer(),
        _xT("XGameEngine Assertion Failed"),
        MB_ABORTRETRYIGNORE|MB_ICONHAND|MB_SETFOREGROUND|MB_TASKMODAL) ;
#endif

      if( nRet == IDABORT ) {
        raise(SIGABRT);
        exit(ASSERTION_EXIT_CODE);
      } else if( nRet == IDRETRY ) {
        // We use this __debugbreak() 
        // which is an intrinsic function 
        // namely __asm int 3
        // The ::DebugBreak won't work on my machine
        // no stacktrace dump!!!
        __debugbreak();
      } else if( nRet == IDIGNORE ) {
        return;
      } else {
        // unreachable ..
      }

      abort();
 
    }

    void Assertion( const char_t* file ,const char_t* func ,size_t line ,const char_t* expression ) {
      Assertion(file,func,line,expression,NULL);
    }

#undef XASSERTION_INTERNAL_ERRCHECK
  } // namespace xge_internal
}// namespace xge